home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1999 #2 / Amiga Plus CD - 1999 - No. 2.iso / System-Boost / Sound / GCSound / source / GCSound.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-11-12  |  14.9 KB  |  479 lines

  1.  
  2. /***********************************************************************
  3. *
  4. *    GCSound 1.0 - D. Keletsekis (October 98)
  5. *
  6. *    compile: SC GCSound  NOSTACKCHECK  LINK  NOSTARTUP
  7. *    ..or have a look at the scoptions files..
  8. *
  9. *    This is a "host" program for playing 8SVX sound samples.
  10. *    It can understand commands from ARexx or Gui4Cli and
  11. *    load & play many samples together, multitaskinglisingly..
  12. *
  13. *    The name of the port opened is "gcsound" (note lower case)
  14. *
  15. *    These are the current commands :
  16. *
  17. *    QUIT    - abort all sounds and quit
  18. *    LOAD    <SampleFile> <Alias> (default speed/time/volume)
  19. *    PLAY    <alias> <times/0> <volume> <speed/-1> 
  20. *    SOUND   <SampleFile> <times/0> <volume> <speed/-1> (load, play & quit)
  21. *    VOLUME  <alias> <1-64>
  22. *    SPEED   <alias> <?>
  23. *    TIMES   <alias> <times/0>
  24. *    INFO    <alias> (return "volume speed")
  25. *    UNLOAD  <alias> (or "" for all samples - stop & unload)
  26. *    STOP    <alias> (or "" for all samples - stop playing)
  27. *
  28. *    From Gui4Cli they can be called either with :
  29. *    -> Call gcsound command arguments..        -or-
  30. *    -> SendRexx gcsound "command arguments"    (note the quotes)
  31. *
  32. ************************************************************************/
  33. #define __USESYSBASE
  34. #include <exec/exec.h>
  35. #include <exec/execbase.h>
  36. #include <exec/memory.h>
  37. #include <dos/dosextens.h>
  38. #include <dos/rdargs.h>
  39. #include <dos/dostags.h>
  40. #include <devices/audio.h>
  41. #include <graphics/gfxbase.h>
  42. #include <string.h>
  43. #include <stdio.h>
  44. #include <ctype.h>
  45. #include <dos.h>
  46. #include <proto/dos.h>
  47. #include <proto/exec.h>
  48. #include <proto/rexxsyslib.h>
  49. #include <graphics/text.h>
  50. #include <rexx/storage.h>
  51. #include <datatypes/soundclass.h>    // include soundclass
  52.  
  53. #include "Gui4Cli.h"        // include Gui4Cli.h (for the message structure)
  54.  
  55. #include "gcsound_protos.h"
  56.  
  57. static const char VERSION[] = "\0$VER: gcsound 1.0 (D.Keletsekis Oct.98)";
  58.  
  59. #define BUFFER_SIZE 65536     // the buffer size (should be a tooltype)
  60. #define CLEANMEM (MEMF_ANY | MEMF_CLEAR | MEMF_PUBLIC)
  61.  
  62. // We use this "treat a 4 character string as if it were a LONG
  63. // integer" ID method for identifying our commands also.
  64. // This means that *only* the first four letters of each command
  65. // are checked - but it allows us to use a fast case statement.
  66. #define MakeID(a,b,c,d)    ((LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d))
  67. #define QUIT     MakeID('Q','U','I','T')
  68. #define LOAD     MakeID('L','O','A','D')
  69. #define PLAY     MakeID('P','L','A','Y')
  70. #define SOUND     MakeID('S','O','U','N')
  71. #define VOLUME     MakeID('V','O','L','U')
  72. #define SPEED     MakeID('S','P','E','E')
  73. #define TIMES     MakeID('T','I','M','E')
  74. #define INFO     MakeID('I','N','F','O')
  75. #define UNLOAD     MakeID('U','N','L','O')
  76. #define STOP     MakeID('S','T','O','P')
  77. // #define ID_FORM    MakeID('F','O','R','M')
  78.  
  79. struct myhandle        // each sample has one of these..
  80. {
  81.    struct VoiceHeader vh;    // the 8SVX header
  82.    LONG   bodystart;        // offset of body start into file
  83.    LONG   bodylength;        // total body length
  84.    ULONG  speed;        // playback speed
  85.    ULONG  volume;        // volume (default=64)
  86.    ULONG  times;        // times to play
  87.    ULONG  played;        // times already played
  88.    UBYTE  *buff1, *buff2;    // the buffer pointers
  89.    LONG   buffsize;        // buffer size
  90.    BPTR   fp;            // the file pointer (if file is open)
  91.    char   path[256];        // full name & path of file
  92.    char   alias[40];        // alias - the given name (upper case)
  93.    struct IOAudio io1, io2;      // Pointers to Audio IO structures
  94.    BOOL   out1, out2;        // flags indicating msgs outstanding
  95.    ULONG  remain;        // remaining data length (large samples)
  96.    BOOL   reload;        // 1 = reload sample before replaying
  97.    BOOL   killflag;        // 1 = kill sample when all msgs replied
  98.    BOOL   playonce;        // 1 = SOUND - load/play/quit sample
  99.    struct base *bs;        // pointer to base
  100.    struct myhandle *next;
  101. };
  102.  
  103. struct base         // somewhere to hold everything
  104. {
  105.    struct myhandle *toph;    // top of handles linked list
  106.    struct MsgPort *soundport;    // our sound reply port
  107.    ULONG  clock;        // clock constant
  108.    UBYTE  retbuff[80];          // return text..
  109.    LONG   users;        // how many guis are using us
  110.    struct DosLibrary *dosbase;    // carry these around..
  111.    struct ExecBase *sysbase;
  112. };
  113.  
  114.  
  115. // ===============================================================
  116. //     MAIN() - note there is no need for a main() function
  117. //    The program starts at the 1st function it finds, which
  118. //    can be any name..
  119. // ===============================================================
  120.  
  121. int main(void)
  122. {
  123. // these are the libraries we need
  124. struct ExecBase *SysBase = (*((struct ExecBase **) 4));
  125. struct DosLibrary *DOSBase=NULL;
  126. struct Process *myself = (struct Process *)(SysBase->ThisTask);
  127. struct MsgPort *myport=NULL;        // our port
  128. ULONG  soundsig, portsig, sig;        // port signal masks
  129. struct g4cmsg  *msg;    // Gui4Cli message pointer
  130. int    rc = 10;     // return code
  131. BOOL   endflag = 0;    // control flags
  132. BOOL   errorflag = 0;
  133. struct base *bs=NULL;    // our base struct pointer
  134. struct myhandle *h, *hh;
  135. struct IOAudio *io;
  136. struct Task *mt=NULL;    // for bumping task priority
  137. BYTE   oldpri=0;
  138. LONG   readmore, ret;
  139. LONG   rxargs[6];        // for rexx msg parsing
  140. struct RDArgs *rdargs;
  141. struct RexxMsg *rxmsg;
  142. struct RexxSysBase *RexxSysBase = NULL;
  143.  
  144. // -------------------- open the dos library or die..
  145.  
  146. if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36L)))
  147. {   myself->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;
  148.     goto endprog;
  149. }
  150.  
  151. // if rexx is not opened, rexx commands will not be executed
  152. RexxSysBase = (struct RexxSysBase *)OpenLibrary ("rexxsyslib.library", 36L);
  153.  
  154. // -------------------- if gcsound is already running ++users & quit
  155.  
  156. if (myport = FindPort("gcsound"))
  157. {   if (msg = (struct g4cmsg *)AllocVec(sizeof(struct g4cmsg), CLEANMEM))
  158.     {   msg->magic = 392002;  // the only thing that's checked
  159.     msg->node.mn_Length = sizeof(struct g4cmsg);
  160.     PutMsg (myport, &msg->node);
  161.     // msg will not be replied & *will* be freeveced
  162.     }
  163.     myport = NULL; // so that it's not freed
  164.     rc = 0;
  165.     goto endprog;
  166. }
  167.  
  168. // -------------------- get our main base structure
  169.  
  170. if (!(bs = (struct base *)AllocVec(sizeof(struct base), CLEANMEM)))
  171.     goto endprog;
  172. bs->sysbase = SysBase;
  173. bs->dosbase = DOSBase;
  174. bs->users   = 1; // the guy who loaded us is also our 1st user
  175. if (!getconstant(bs)) goto endprog;
  176.  
  177. // --------------------- increase our priority - why ?
  178.  
  179. mt = FindTask(NULL);
  180. oldpri = SetTaskPri(mt,21);
  181.  
  182. // --------------------- Open message ports or die..
  183.  
  184. bs->soundport = CreatePort(0,0);    // sound return port
  185. myport = openport ("gcsound", SysBase, DOSBase);  // G4C port
  186. if (!bs->soundport || !myport)
  187. {   PutStr ("Couldn't open ports!\n");
  188.     goto endprog;
  189. }
  190. portsig  = (1 << myport->mp_SigBit);   // create signal masks
  191. soundsig = (1 << bs->soundport->mp_SigBit);
  192.  
  193. // ---------------------- Main wait() & process loop
  194. // quit on endflag but after all samples have quit first
  195.  
  196. while ((!endflag) || (bs->toph))
  197. {
  198.    // wait on combined port signals
  199.    sig = Wait (portsig | soundsig);
  200.  
  201.    // ------------------- messages from Gui4Cli, ARexx etc
  202.  
  203.    // we support messages from an other instance of this program, or
  204.    // from Gui4Cli or from ARexx - we check to see what we got
  205.  
  206.    if (sig & portsig)
  207.    {
  208.        while (msg = (struct g4cmsg *)GetMsg(myport))
  209.        {
  210.        // if it's from an other instance of gcsound, ++users
  211.        if (msg->magic == 392002)
  212.        {   ++bs->users;
  213.            FreeVec (msg);  // do *not* reply - *do* FreeVec()
  214.        }
  215.  
  216.        else if (msg->magic == 392001)  // from gui4cli
  217.        {
  218.                // check that its like we expect it..
  219.                if ((msg->type == GM_COMMAND) && msg->gcmain && msg->com)
  220.                {
  221.                   ret = docommand ((*((LONG *)msg->com)), bs, msg->args[0], msg->args[1], msg->args[2], msg->args[3]);
  222.                   if (ret < 0) endflag = 1;       // quit
  223.                   else 
  224.                   {   // if there is a return, attach string
  225.                       if (bs->retbuff[0])
  226.                       {   if (msg->msgret = (UBYTE *)AllocVec(80, CLEANMEM))
  227.                               strcpy (msg->msgret, bs->retbuff);
  228.                           bs->retbuff[0] = '\0';
  229.                       }
  230.                       msg->res = ret;
  231.                   }  
  232.                }
  233.                else msg->res = 20;  // indicate error
  234.  
  235.                // reply the message to Gui4Cli
  236.                ReplyMsg ((struct Message *)msg);
  237.            }
  238.  
  239.            else if (RexxSysBase) // try arexx message
  240.            {
  241.                rxmsg = (struct RexxMsg *)msg;
  242.                if ((rxmsg->rm_Action & RXCODEMASK)==RXCOMM)  // it's a rexx command
  243.                {
  244.            rdargs = readargs (rxargs, 5, rxmsg->rm_Args[0], "COM/A,A0,A1,A2,A3", bs);
  245.            if (rdargs)
  246.            {
  247.                makeupper ((UBYTE *)rxargs[0]);
  248.                ret = docommand ((*((LONG *)rxargs[0])), bs, (UBYTE *)rxargs[1], (UBYTE *)rxargs[2], (UBYTE *)rxargs[3], (UBYTE *)rxargs[4]);
  249.                if (ret < 0) endflag = 1;   // quit
  250.                else
  251.                {  if (bs->retbuff[0]) 
  252.                   {  rxmsg->rm_Result2 = (LONG)CreateArgstring(bs->retbuff, strlen(bs->retbuff));
  253.                      bs->retbuff[0] = '\0';
  254.                   }
  255.                   rxmsg->rm_Result1 = ret;
  256.                }
  257.                // free the arguments we got
  258.                freeargs (rdargs, bs);
  259.            }
  260.            else rxmsg->rm_Result1 = 10; // return error
  261.                }
  262.                ReplyMsg ((struct Message *)msg);
  263.            }
  264.  
  265.            else ReplyMsg ((struct Message *)msg); // always reply
  266.  
  267.        }   // end of while getmsg()
  268.    }       // end of command port handling
  269.  
  270.    // -------------------- message from audio device
  271.  
  272.    else if (sig & soundsig)
  273.    {
  274.        errorflag = 0;
  275.        while ((io = (struct IOAudio *)GetMsg (bs->soundport)) && !errorflag)
  276.        {
  277.        // if it's a volume/speed command, free it & finished
  278.        if (io->ioa_Request.io_Command == ADCMD_PERVOL)
  279.        {   FreeVec (io);
  280.            goto skipaudio;
  281.        }
  282.  
  283.        // find the audio structure
  284.        if (!(h = findaudio (io, bs->toph)))
  285.        {   PutStr ("What was that?!\n");
  286.            goto skipaudio;
  287.        }
  288.  
  289.        if (h->fp == NULL)    // file closed => small sample
  290.        {   // finished - message has played and returned..
  291.            h->out1 = 0;
  292.            CloseDevice ((struct IORequest *)&h->io1);
  293.             if (h->killflag)
  294.            {   remlink (h);
  295.            freehandle (h);
  296.            h = NULL;
  297.            }
  298.            goto skipaudio;
  299.        }
  300.  
  301.        // ----------- read in buffer for long samples
  302.  
  303.        // adjust the "message outstanding" flag
  304.        if (io == &h->io1) h->out1 = 0;
  305.        else h->out2 = 0;
  306.        
  307.        // if no data remaining, we've finished playing..
  308.        // deal with aborted messages
  309.        if (h->remain <= 0)
  310.        {
  311.           // if both sample parts have returned..
  312.           if ((h->out1==0) && (h->out2==0))
  313.           {
  314.           // Printf ("-> Closing device on %s\n", h->alias);
  315.           // close up & kill sample if so marked
  316.           CloseDevice ((struct IORequest *)&h->io1);
  317.            if (h->killflag)
  318.               {   remlink (h);
  319.               // Printf ("-> Freeing %s\n", h->alias);
  320.               freehandle (h);
  321.               h = NULL;
  322.           }   }
  323.           goto skipaudio;
  324.        }
  325.  
  326.        // if there is little remmaining, start again as needed
  327.        if (h->remain < h->buffsize)
  328.        {
  329.           // read to end of data..
  330.           io->ioa_Length = Read (h->fp, io->ioa_Data, h->remain);
  331.           if (io->ioa_Length != h->remain)
  332.           {   PutStr ("Error reading file\n");
  333.               errorflag = 1;
  334.           }
  335.           
  336.           // if we still have times to play (0=forever)..
  337.           ++h->played;
  338.           if ((h->times == 0) || (h->played < h->times))
  339.           {
  340.                  // how much to read from file start
  341.                  readmore = h->buffsize - h->remain;
  342.                  Seek (h->fp, h->bodystart, OFFSET_BEGINNING);
  343.  
  344.                  // read in rest of buffer
  345.                  io->ioa_Length += Read (h->fp, &io->ioa_Data[h->remain], readmore);
  346.                  if (io->ioa_Length != h->buffsize)
  347.                  {   PutStr ("Error reading file\n");
  348.                      errorflag = 1;
  349.                  }
  350.                  h->remain = h->bodylength - readmore;
  351.               }
  352.               else h->remain = 0;
  353.        }
  354.        
  355.        // otherwise plain read it in..
  356.        else
  357.            {  io->ioa_Length = Read (h->fp, io->ioa_Data, h->buffsize);
  358.           if (io->ioa_Length != h->buffsize)
  359.           {   PutStr ("Error reading file\n");
  360.               errorflag = 1;
  361.           }
  362.           h->remain -= h->buffsize;
  363.        }
  364.  
  365.        if (!errorflag)
  366.        {  // adjust the "message outstanding" flag
  367.           if (io == &h->io1) h->out1 = 1;
  368.           else h->out2 = 1;
  369.  
  370.           // send out message again..
  371.               BeginIO((struct IORequest *)io);
  372.            }
  373.            else 
  374.           CloseDevice ((struct IORequest *)&h->io1);
  375.            
  376.            skipaudio: 
  377.         ;
  378.  
  379.        } // end of while getmsg()
  380.  
  381.    } // end of audio handling
  382.  
  383. }  // end of main while(!endflag) loop
  384.  
  385. // set return code
  386. if (endflag > 1) rc = endflag;   // 1 = ok quit
  387. else rc = 0;
  388.  
  389. // ---------------------- END PROG - CLEAN UP
  390.  
  391. endprog :
  392.  
  393. if (bs)
  394. {  if (bs->toph)  
  395.    {   h = bs->toph;   // free all handles
  396.        while (h)
  397.        {   hh = h;  
  398.            h = h->next;
  399.            freehandle (hh);
  400.    }   }
  401.    if (bs->soundport) DeleteMsgPort (bs->soundport);
  402.    FreeVec (bs);
  403. }
  404.  
  405. if (myport)      closeport (myport, SysBase, DOSBase);
  406. if (mt)          SetTaskPri (mt, oldpri);
  407. if (RexxSysBase) CloseLibrary ((struct Library *)RexxSysBase);
  408. if (DOSBase)     CloseLibrary ((struct Library *)DOSBase);
  409. return (rc);
  410. }
  411.  
  412. // ================================================================
  413. //     create a new public message port
  414. //    Note we pass our library bases as arguments since we'll
  415. //    use functions that are included therein. Actually, we
  416. //    only need to pass SysBase, since there are no dos.library
  417. //    functions called here (AFAIK), but what the hey..
  418. // ================================================================
  419.  
  420. struct MsgPort *openport (char *portname, struct ExecBase *SysBase, struct DosLibrary *DOSBase)
  421. {
  422.    struct MsgPort *port=NULL;
  423.  
  424.    Forbid ();
  425.    if ((port = FindPort(portname)) != NULL)  
  426.    {    // if port already exists - return NULL
  427.         port = NULL;
  428.    }
  429.    else
  430.         port = CreatePort (portname, 0);
  431.    Permit();
  432.  
  433.    return (port);
  434. }
  435.  
  436. // ================================================================
  437. //     free a public message port
  438. // ================================================================
  439.  
  440. void closeport (struct MsgPort *port, struct ExecBase *SysBase, struct DosLibrary *DOSBase)
  441. {
  442.    struct Message *msg;
  443.  
  444.    Forbid ();
  445.    // empty port
  446.    while (msg = GetMsg (port))
  447.           ReplyMsg (msg);
  448.    // remove port name since it's a public msg port
  449.    if (port->mp_Node.ln_Name)
  450.           RemPort (port);
  451.    // delete port
  452.    DeleteMsgPort (port);
  453.    Permit ();
  454. }
  455.  
  456. // ===============================================================
  457. //    find the handle from an audio struct
  458. // ===============================================================
  459. struct myhandle *findaudio (struct IOAudio *io, struct myhandle *h)
  460. {
  461.    while (h)
  462.    {  if ((io == &h->io1) || (io == &h->io2)) return (h);
  463.       h = h->next;
  464.    }
  465.    return (NULL);
  466. }
  467.  
  468. // ===============================================================
  469. // include other files here, so they are after the main function
  470. #include "docommands.h"
  471. #include "sound.h"
  472. #include "iff.h"
  473. #include "ReadArgs.h"
  474.  
  475.  
  476.  
  477.  
  478.  
  479.